package com.fjwt.gz.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjwt.gz.core.cache.RedisUtil;
import com.fjwt.gz.core.constants.Constants;
import com.fjwt.gz.core.exception.BizException;
import com.fjwt.gz.core.model.security.GzUserDetails;
import com.fjwt.gz.db.dto.BlacklistDTO;
import com.fjwt.gz.db.entity.AgentInfoEntity;
import com.fjwt.gz.db.entity.BlacklistEntity;
import com.fjwt.gz.db.entity.BlacklistRuleEntity;
import com.fjwt.gz.db.entity.OssRecordEntity;
import com.fjwt.gz.db.vo.BlacklistVO;
import com.fjwt.gz.service.mapper.BlacklistMapper;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 黑名单明细表 服务实现类
 * </p>
 *
 * @author wangjie
 * @since 2025-05-12
 */
@Slf4j
@Service
public class BlacklistService extends ServiceImpl<BlacklistMapper, BlacklistEntity> {

    @Autowired
    private BlacklistRuleService blacklistRuleService;

    @Autowired
    private AgentInfoService agentInfoService;

    @Autowired
    private OssRecordService ossRecordService;

    /**
     * 黑名单列表
     *
     * @param iPage
     * @param blacklistDTO
     * @return
     */
    public IPage<BlacklistVO> getBlacklistList(IPage iPage, BlacklistDTO blacklistDTO) {
        Long blacklistRuleId = blacklistDTO.getBlacklistRuleId();
        if (blacklistRuleId == null) {
            throw new BizException("请选择规则名称后进行查询");
        }
        BlacklistRuleEntity ruleEntity = blacklistRuleService.getById(blacklistRuleId);
        if (ruleEntity == null) {
            throw new BizException("无当前黑名单规则");
        }
        Integer ruleType = ruleEntity.getRuleType();
        if (ruleType == 1) {
            return baseMapper.getBlacklistListByRuleId(iPage, blacklistDTO);
        }
        //限制类型若为客户经理
        String agentNo = ruleEntity.getAgentNo();
        AgentInfoEntity agentInfo = agentInfoService.getAgentInfo(agentNo);

        return baseMapper.getBlacklistInfoByFansIdList(iPage, agentInfo, blacklistDTO);
    }


    /**
     * 根据黑名单规则id获取黑名单列表
     **/
    public Map<String, BlacklistEntity> getBlackListByRuleId(BlacklistRuleEntity blackListRule, Long redisOutTime) {
        Long blacklistRuleId = blackListRule.getBlacklistRuleId();
        String activityBlacklistKey = String.format("%s%s:%s", Constants.REDIS_ACTIVITY.BLACK, Constants.REDIS_ACTIVITY.BLACKLIST, blacklistRuleId);
        Map<String, BlacklistEntity> blackListEntityHashMaps = RedisUtil.getObject(activityBlacklistKey, new com.alibaba.fastjson2.TypeReference<Map<String, BlacklistEntity>>() {
        });
        log.info("黑名单规则id:{}，redis中获取对应黑名单列表：{}", blacklistRuleId, JSONUtil.toJsonStr(blackListEntityHashMaps));
        if (ObjectUtils.isNotEmpty(blackListEntityHashMaps)) {
            return blackListEntityHashMaps;
        }
        //查询数据库
        LambdaQueryWrapper<BlacklistEntity> gw = BlacklistEntity.gw();
        gw.eq(BlacklistEntity::getBlacklistRuleId, blacklistRuleId);
        List<BlacklistEntity> blackListEntities = baseMapper.selectList(gw);
        if (CollUtil.isEmpty(blackListEntities)) {
            return new HashMap<>();
        }
        blackListEntityHashMaps = blackListEntities.stream()
                .collect(Collectors.toMap(BlacklistEntity::getCheckField, blackListEntity -> blackListEntity));
        log.info("黑名单规则id:{}，数据库查询对应黑名单列表：{}", blacklistRuleId, JSONUtil.toJsonStr(blackListEntityHashMaps));
        //放入redis中
        RedisUtil.set(activityBlacklistKey, blackListEntityHashMaps, redisOutTime, TimeUnit.MILLISECONDS);

        return blackListEntityHashMaps;
    }

    /**
     * 获取客户经理的黑名单列表
     **/
    public Map<String, BlacklistVO> getCMBlackList(BlacklistRuleEntity blackListRule, Long redisOutTime) {
        Long blacklistRuleId = blackListRule.getBlacklistRuleId();
        String activityBlacklistKey = String.format("%s%s:%s", Constants.REDIS_ACTIVITY.BLACK, Constants.REDIS_ACTIVITY.EMPLOYEE_BLACKLIST, blacklistRuleId);
        Map<String, BlacklistVO> blackListEntityHashMaps = RedisUtil.getObject(activityBlacklistKey, new com.alibaba.fastjson2.TypeReference<Map<String, BlacklistVO>>() {
        });
        log.info("客户经理类型黑名单规则id:{}，redis中获取对应黑名单列表：{}", blacklistRuleId, JSONUtil.toJsonStr(blackListEntityHashMaps));
        if (ObjectUtils.isNotEmpty(blackListEntityHashMaps)) {
            return blackListEntityHashMaps;
        }
        //限制类型若为客户经理
        String agentNo = blackListRule.getAgentNo();
        AgentInfoEntity agentInfo = agentInfoService.getAgentInfo(agentNo);

        List<BlacklistVO> blacklist = baseMapper.getBlacklistInfoByFansIdList(agentInfo, new BlacklistDTO());
        Map<String, BlacklistVO> blacklistVOMap = blacklist.stream()
                .collect(Collectors.toMap(BlacklistVO::getEmployeeNo, blacklistVO -> blacklistVO));
        log.info("客户经理类型黑名单规则id:{}，数据库查询对应黑名单列表：{}", blacklistRuleId, JSONUtil.toJsonStr(blacklistVOMap));
        //放入redis中
        RedisUtil.set(activityBlacklistKey, blacklistVOMap, redisOutTime, TimeUnit.MILLISECONDS);

        return blacklistVOMap;
    }

    /**
     * 黑名单导入
     *
     * @param blacklistDTO
     * @return
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void importBlacklistList(BlacklistDTO blacklistDTO, String bizType) {
        // 参数校验
        if (StrUtil.isBlank(blacklistDTO.getOssFileUrl()) || blacklistDTO.getBlacklistRuleId() == null) {
            throw new BizException("参数信息有误");
        }
        
        // 获取黑名单规则
        BlacklistRuleEntity ruleEntity = blacklistRuleService.getById(blacklistDTO.getBlacklistRuleId());
        if (ruleEntity == null) {
            throw new BizException("无当前黑名单规则");
        }
    
        // 读取文件数据
        List<BlacklistVO> blacklist;
        try {
            blacklist = getBlacklist(blacklistDTO.getOssFileUrl(), bizType, ruleEntity);
        } catch (Exception e) {
            log.error("读取黑名单文件失败，URL: {}, 错误: {}", blacklistDTO.getOssFileUrl(), e.getMessage());
            throw new BizException("文件读取失败");
        }
        
        if (CollUtil.isEmpty(blacklist)) {
            throw new BizException("导入文件数据为空");
        }
    
        String checkField = ruleEntity.getCheckField();
    
        // 查询已存在的黑名单列表
        List<BlacklistEntity> existList = list(BlacklistEntity.gw().eq(BlacklistEntity::getBlacklistRuleId, blacklistDTO.getBlacklistRuleId()));
        Set<String> checkFieldSet = existList.stream()
            .map(BlacklistEntity::getCheckField)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());
        
        List<BlacklistEntity> saveList = new ArrayList<>();
        JSONObject extraJson1 = parseJsonObject(ruleEntity.getExtraField1());
        JSONObject extraJson2 = parseJsonObject(ruleEntity.getExtraField2());
        JSONObject extraJson3 = parseJsonObject(ruleEntity.getExtraField3());
        JSONObject extraJson4 = parseJsonObject(ruleEntity.getExtraField4());
    
        for (BlacklistVO blacklistVO : blacklist) {
            String checkValue = blacklistVO.getCheckField();
            if (StrUtil.isBlank(checkValue)) {
                throw new BizException("校验字段值为空");
            }

            if ("phone".equals(checkField) && !PhoneUtil.isMobile(checkValue)) {
                log.info("存在手机号格式错误, 手机号：{}", checkValue);
                throw new BizException("存在手机号格式错误");
            }
            if ("cstmNo".equals(checkField) && StrUtil.isBlank(checkValue)) {
                log.info("存在实名ID为空, 实名ID：{}", checkValue);
                throw new BizException("存在实名ID为空");
            }
            //过滤重复数据
            if (checkFieldSet.contains(checkValue)) {
                continue;
            }
            checkFieldSet.add(checkValue);
    
            BlacklistEntity entity = new BlacklistEntity();
            entity.setCheckField(checkValue);
    
            // 校验必填字段
            validateExtraField(blacklistVO, extraJson1, 1);
            validateExtraField(blacklistVO, extraJson2, 2);
            validateExtraField(blacklistVO, extraJson3, 3);
            validateExtraField(blacklistVO, extraJson4, 4);
    
            entity.setExtraField1(blacklistVO.getExtraField1());
            entity.setExtraField2(blacklistVO.getExtraField2());
            entity.setExtraField3(blacklistVO.getExtraField3());
            entity.setExtraField4(blacklistVO.getExtraField4());
            entity.setBlacklistRuleId(blacklistDTO.getBlacklistRuleId());
            entity.setCreatedUid(GzUserDetails.getCurrentUserDetails().getSysUserId());
            saveList.add(entity);
        }
        
        // 批量保存
        if (CollUtil.isNotEmpty(saveList)) {
            int batchSize = 1000;
            for (int i = 0; i < saveList.size(); i += batchSize) {
                int end = Math.min(i + batchSize, saveList.size());
                saveBatch(saveList.subList(i, end));
            }
            clearCache(blacklistDTO.getBlacklistRuleId());
            log.info("成功导入黑名单数据，规则ID: {}, 数量: {}", blacklistDTO.getBlacklistRuleId(), saveList.size());
        } else {
            log.warn("无新增黑名单数据，规则ID: {}", blacklistDTO.getBlacklistRuleId());
        }
    }

    private JSONObject parseJsonObject(String json) {
        if (StrUtil.isBlank(json)) {
            return null;
        }
        try {
            return JSONObject.parseObject(json);
        } catch (JSONException e) {
            log.warn("JSON 解析失败: {}", json, e);
            return null;
        }
    }
    
    // 校验额外字段必填逻辑
    private void validateExtraField(BlacklistVO vo, JSONObject extraJson, int fieldIndex) {
        if (extraJson == null) {
            return;
        }
        boolean isMust = extraJson.getBoolean("isMust");
        if (isMust) {
            String value = null;
            switch (fieldIndex) {
                case 1: value = vo.getExtraField1(); break;
                case 2: value = vo.getExtraField2(); break;
                case 3: value = vo.getExtraField3(); break;
                case 4: value = vo.getExtraField4(); break;
            }
            if (StrUtil.isBlank(value)) {
                String fieldName = extraJson.getString("fieldName");
                throw new BizException("存在必填字段 " + fieldName + " 为空");
            }
        }
    }
    /**
     * oss文件导入
     *
     * @return
     */
    public List<BlacklistVO> getBlacklist(String ossFileUrl, String bizType, BlacklistRuleEntity ruleEntity) {
        ExcelReader reader = null;
        Path tempFile = null;
        try {
            // 创建临时文件
            tempFile = Files.createTempFile("blacklist-", ".xlsx");
            File file = tempFile.toFile();

            // 下载文件
            HttpUtil.downloadFile(ossFileUrl, file);

            // 添加OSS上传记录
            OssRecordEntity ossRecordEntity = new OssRecordEntity();
            ossRecordEntity.setBizType(bizType);
            ossRecordEntity.setOssUrl(ossFileUrl);
            ossRecordEntity.setFileSize(file.length());
            ossRecordEntity.setUploadType(Constants.OSS_UPLOAD_TYPE.BLACKLIST_IMPORT);
            ossRecordEntity.setSysUserId(GzUserDetails.getCurrentUserDetails().getSysUserId());
            ossRecordEntity.setCreatedAt(new Date());
            ossRecordService.save(ossRecordEntity);

            // 读取文件内容
            reader = ExcelUtil.getReader(file);
            String checkField = ruleEntity.getCheckField();
            String phoneHeader = "手机号";
            String idHeader = "实名唯一ID";
            reader.addHeaderAlias("phone".equals(checkField) ? phoneHeader : idHeader, "checkField");

            // 处理extraField
            processExtraField(reader, ruleEntity.getExtraField1(), "extraField1");
            processExtraField(reader, ruleEntity.getExtraField2(), "extraField2");
            processExtraField(reader, ruleEntity.getExtraField3(), "extraField3");
            processExtraField(reader, ruleEntity.getExtraField4(), "extraField4");

            return reader.readAll(BlacklistVO.class);
        } catch (Exception e) {
            log.error("黑名单导入失败: {}", e.getMessage(), e);
            throw new BizException("导入黑名单失败");
        } finally {
            IoUtil.close(reader);
            if (tempFile != null) {
                FileUtil.del(tempFile.toFile());
            }
        }
    }

    // 封装extraField处理逻辑
    private void processExtraField(ExcelReader reader, String jsonStr, String fieldName) {
        if (StrUtil.isNotBlank(jsonStr)) {
            JSONObject jsonObject = JSON.parseObject(jsonStr);
            if (jsonObject.containsKey("fieldName")) {
                reader.addHeaderAlias(jsonObject.getString("fieldName"), fieldName);
            }
        }
    }

    private void clearCache(Long blacklistRuleId) {
        String activityBlacklistKey = String.format("%s%s:%s", Constants.REDIS_ACTIVITY.BLACK, Constants.REDIS_ACTIVITY.BLACKLIST, blacklistRuleId);
        RedisUtil.del(activityBlacklistKey);
    }


    /**
     * 黑名单导出列表
     *
     * @param blacklistDTO
     * @return
     */
    public List<BlacklistVO> exportBlacklistList(BlacklistDTO blacklistDTO, BlacklistRuleEntity ruleEntity) {

        Integer ruleType = ruleEntity.getRuleType();
        if (ruleType == 1) {
            return baseMapper.getBlacklistListByRuleId(blacklistDTO);
        }
        //限制类型若为客户经理
        String agentNo = ruleEntity.getAgentNo();
        AgentInfoEntity agentInfo = agentInfoService.getAgentInfo(agentNo);
        //获取当前机构及机构下的所有客户经理
        return baseMapper.getBlacklistInfoByFansIdList(agentInfo, blacklistDTO);
    }

    /**
     * 黑名单导出
     *
     * @param blacklistDTO
     * @param response
     */
    public void exportBlacklistByExcel(BlacklistDTO blacklistDTO, HttpServletResponse response) {
        Long blacklistRuleId = blacklistDTO.getBlacklistRuleId();
        if (blacklistRuleId == null) {
            throw new BizException("请选择规则后进行导出");
        }
        BlacklistRuleEntity ruleEntity = blacklistRuleService.getById(blacklistRuleId);
        if (ruleEntity == null) {
            throw new BizException("无当前黑名单规则");
        }
        List<BlacklistVO> blacklistVOList = exportBlacklistList(blacklistDTO, ruleEntity);
        if (CollUtil.isEmpty(blacklistVOList)) {
            throw new BizException("无黑名单数据可导出");
        }
        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.ms-excel");
        try {
            String fileName = "黑名单列表.xlsx";
            String encodedFileName = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
            response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName);
        } catch (Exception e) {
            throw new BizException("系统异常");
        }

        ExcelWriter writer = ExcelUtil.getWriter(true);
        writer.renameSheet("黑名单列表");
        if (ruleEntity.getRuleType() == 0) {
            //若是客户经理
            writer.addHeaderAlias("phone", "手机号");
            writer.addHeaderAlias("name", "姓名");
            writer.addHeaderAlias("agentNo", "归属机构");
        } else {
            String checkField = ruleEntity.getCheckField();
            writer.addHeaderAlias("checkField", "phone".equals(checkField) ? "手机号" : "实名唯一ID");

            for (int i = 1; i <= 4; i++) {
                String extraField = (String) ReflectUtil.getFieldValue(ruleEntity, "extraField" + i);
                if (StrUtil.isNotBlank(extraField)) {
                    JSONObject jsonObject = JSON.parseObject(extraField);
                    writer.addHeaderAlias("extraField" + i, jsonObject.getString("fieldName"));
                }
            }
        }

        writer.setOnlyAlias(true);
    
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            writer.write(blacklistVOList);
            writer.flush(outputStream);
        } catch (IOException e) {
            throw new BizException("文件输出异常");
        } finally {
            writer.close();
        }
    }

    /**
     * 删除黑名单列表
     *
     * @param blacklistId
     */
    public void removeByListId(Long blacklistId) {
        BlacklistEntity blacklist = getById(blacklistId);
        if (blacklist == null) {
            throw new BizException("无当前人员");
        }
        Long blacklistRuleId = blacklist.getBlacklistRuleId();
        removeById(blacklistId);
        clearCache(blacklistRuleId);
    }

    private void clearCmCache(Long blacklistRuleId) {
        String activityBlacklistKey = String.format("%s%s:%s", Constants.REDIS_ACTIVITY.BLACK, Constants.REDIS_ACTIVITY.EMPLOYEE_BLACKLIST, blacklistRuleId);
        RedisUtil.del(activityBlacklistKey);
    }

    /**
     * 删除黑名单缓存
     *
     * @param agentInfo
     */
    /**
     * 删除黑名单缓存
     */
    public void deleteBlacklistCache() {
        //查询客户经理黑名单列表
        List<BlacklistRuleEntity> list = blacklistRuleService.list(BlacklistRuleEntity.gw()
                .eq(BlacklistRuleEntity::getRuleType, 0));
        if (CollUtil.isEmpty(list)) {
            return;
        }
        for (BlacklistRuleEntity blacklistRuleEntity : list) {
            clearCmCache(blacklistRuleEntity.getBlacklistRuleId());
        }
    }

}
